package com.jrx.anytxn.atom.service.impl;

import com.jrx.anytxn.atom.bean.CustBatchSaveBean;
import com.jrx.anytxn.common.annotation.MethodType;
import com.jrx.anytxn.common.constant.CommonContants;
import com.jrx.anytxn.common.uitl.BeanMapping;
import io.netty.util.concurrent.FastThreadLocal;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * @program: anytxn-cf-parent
 * @description:
 * @author: xiaoyu.jing
 * @date: 2020-08-31 15:35
 **/
public class CustBatchHelper {

    private static Method[] methods;

    private static final Map<String, Method> getIdMethodCache = new ConcurrentHashMap<>();

    static {
        methods = ReflectionUtils.getAllDeclaredMethods(CustBatchSaveBean.class);
        Arrays.stream(methods).forEach(method -> method.setAccessible(true));
    }

    private static final FastThreadLocal<CustBatchSaveBean> CUST_BATCH_LOCAL = new FastThreadLocal<>();

    public static void setCustBatchLocal(CustBatchSaveBean custBatchSaveBean) {
        CUST_BATCH_LOCAL.set(custBatchSaveBean);
    }

    public static CustBatchSaveBean getCustBatchLocal() {
        return CUST_BATCH_LOCAL.get();
    }

    /**
     * 移除本地变量
     */
    public static void clearCustBatchLocal() {
        CUST_BATCH_LOCAL.remove();
    }

    /**
     * 处理需要更新的对象
     * @param updateList
     * @throws Exception
     */
    public static void dealUpdateList(List updateList) throws Exception {
        if (CollectionUtils.isEmpty(updateList)) {
            return;
        }

        CustBatchSaveBean custBatchSaveBean = CUST_BATCH_LOCAL.get();
        for (Object obj : updateList) {
            if (null == obj) {
                continue;
            }

            //获取obj中与method的返回类型相同的方法
            Predicate<Method> getListMethodPre = method -> {
                String typeName = obj.getClass().getTypeName();
                java.lang.reflect.Type returnType = method.getGenericReturnType();
                if (!(returnType instanceof ParameterizedType)) {
                    return false;
                }

                ParameterizedType paramType = (ParameterizedType) returnType;
                String type = paramType.getActualTypeArguments()[0].getTypeName();
                return typeName.equals(type);
            };

            //获取custBatchSaveBean对象中所有的getXXXList方法 即getSaveXXXList getUpdateXXXList getDealXXXList
            List<Method> getListMethodList = Arrays.stream(methods).filter(getListMethodPre).collect(Collectors.toList());
            //这里是特殊处理，目的是为了最后获取getUpdateXXXList
            getListMethodList.sort(Comparator.comparing(Method::getName).reversed());

            //获取obj对象getId的方法
            Method getIdMethod;
            if (getIdMethodCache.containsKey(obj.getClass().getName())) {
                getIdMethod = getIdMethodCache.get(obj.getClass().getName());
            } else {
                getIdMethod = obj.getClass().getMethod("getId");
                getIdMethod.setAccessible(true);
                getIdMethodCache.put(obj.getClass().getName(), getIdMethod);
            }

            boolean newUpdate = true;
            List orgiUpdateList = new ArrayList<>();
            for (Method getListMethod : getListMethodList) {
                //获取custBatchSaveBean对象原来的List 即saveXXXList updateXXXList dealXXXList
                List orgiList = (List) getListMethod.invoke(custBatchSaveBean);
                String methodType = getListMethod.getAnnotation(MethodType.class).value();
                //如果方法标记为updateXXXList，则赋值orgiUpdateList
                if (CommonContants.METHOD_TYPE_UPDATE.equals(methodType)) {
                    orgiUpdateList = orgiList;
                }
                //轮询原来的List
                for (Object orgi : orgiList) {
                    Long orgId = (Long) getIdMethod.invoke(orgi);
                    Long updateId = (Long) getIdMethod.invoke(obj);
                    //如果id一样则更新原对象字段
                    if (orgId.longValue() == updateId.longValue()) {
                        BeanMapping.copyUpdate(orgi, obj);
                        //如果saveXXXList或者updateXXXList 已有该对象 则表示该对象在之前的job已被更新部分字段，该对象不再需要新加入到updateXXXList
                        if (!CommonContants.METHOD_TYPE_DEAL.equals(methodType)) {
                            newUpdate = false;
                        }
                        break;
                    }
                }
            }

            if (newUpdate) {
                //否则加入到updateXXXList
                orgiUpdateList.add(obj);
            }
        }
    }

    /**
     * 处理需要保存的对象
     * @param saveList
     * @throws Exception
     */
    public static void dealSaveList(List saveList) throws Exception {
        if (CollectionUtils.isEmpty(saveList)) {
            return;
        }

        //获取custBatchSaveBean对象中所有setSaveXXXList,setDealXXXList方法
        CustBatchSaveBean custBatchSaveBean = CUST_BATCH_LOCAL.get();
        Predicate<Method> getList = getListMethod -> {
            java.lang.reflect.Type[] parameterTypes = getListMethod.getGenericParameterTypes();
            if (parameterTypes.length <= 0) {
                return false;
            }
            if (!(parameterTypes[0] instanceof ParameterizedType)) {
                return false;
            }

            ParameterizedType paramType = (ParameterizedType) parameterTypes[0];
            String parameterType = paramType.getActualTypeArguments()[0].getTypeName();

            return saveList.get(0).getClass().getTypeName().equals(parameterType);
        };

        //将需要保存的对象放入saveXXXList,DealXXXList
        List<Method> setListMethodList = Arrays.stream(methods).filter(getList).collect(Collectors.toList());
        for (Method setListMethod : setListMethodList) {
            setListMethod.invoke(custBatchSaveBean, saveList);
        }
    }
}
